package service

import (
	"context"
	"errors"
	"go.uber.org/zap"
	"golang.org/x/crypto/bcrypt"
	"webook/internal/domain"
	"webook/internal/repository"
)

var (
	ErrDuplicateEmail           = repository.ErrDuplicateUser
	ErrInvalidUserOrPassword    = errors.New("用户不存在或密码错误")
	ErrUserNameOrProfileTooLong = repository.ErrUserNameOrProfileTooLong
)

type UserService interface {
	SignUp(ctx context.Context, u domain.User) error
	Login(ctx context.Context, email string, password string) (domain.User, error)
	Edit(ctx context.Context, user domain.User) error
	FindByUserId(ctx context.Context, idInt int64) (domain.User, error)
	FindOrCreate(ctx context.Context, phone string) (domain.User, error)
	FindOrCreateByWechat(ctx context.Context, info domain.WechatInfo) (domain.User, error)
}

type userService struct {
	repo   repository.UserRepository //用指针的原因：整个应用只会有这一个repository
	logger *zap.Logger
}

func NewUserService(repo repository.UserRepository) UserService {
	return &userService{
		repo:   repo,
		logger: zap.L(),
	}
}

// SignUp 名字跟业务概念严格保持一致
func (svc *userService) SignUp(ctx context.Context, u domain.User) error {
	hash, err := bcrypt.GenerateFromPassword([]byte(u.Password), bcrypt.DefaultCost)
	if err != nil {
		return err
	}
	u.Password = string(hash)
	return svc.repo.Create(ctx, u)
}

func (svc *userService) Login(ctx context.Context, email string, password string) (domain.User, error) {
	u, err := svc.repo.FindByEmail(ctx, email)
	if errors.Is(err, repository.ErrUserNotFound) {
		return domain.User{}, ErrInvalidUserOrPassword
	}
	//if err == repository.ErrUserNotFound {
	//	return domain.User{},ErrInvalidUserOrPassword
	//}
	if err != nil {
		return domain.User{}, err
	}
	//检查密码是否正确
	err = bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
	if err != nil {
		return domain.User{}, ErrInvalidUserOrPassword
	}
	return u, nil
}

func (svc *userService) Edit(ctx context.Context, user domain.User) error {
	return svc.repo.Update(ctx, user)
}

func (svc *userService) FindByUserId(ctx context.Context, idInt int64) (domain.User, error) {
	return svc.repo.FindById(ctx, idInt)
}

func (svc *userService) FindOrCreate(ctx context.Context, phone string) (domain.User, error) {
	//认为大部分用户是已经存在的用户
	u, err := svc.repo.FindByPhone(ctx, phone)
	if !errors.Is(err, repository.ErrUserNotFound) {
		//1.err == nil u可用
		//2.err != nil 系统错误
		return u, err
	}
	err = svc.repo.Create(ctx, domain.User{Phone: phone})
	//1.err恰好是phone唯一索引冲突 2.err != nil 系统错误
	if err != nil && !errors.Is(err, repository.ErrDuplicateUser) {
		return domain.User{}, err
	}
	//err = nil 或 ErrDuplicateUser 都代表用户存在
	//不一定能找得到 数据库主从延迟 理论上强制走主库 todo
	return svc.repo.FindByPhone(ctx, phone)
}
func (svc *userService) FindOrCreateByWechat(ctx context.Context, info domain.WechatInfo) (domain.User, error) {

	u, err := svc.repo.FindByWechat(ctx, info.OpenId)
	if !errors.Is(err, repository.ErrUserNotFound) {
		return u, err
	}
	svc.logger.Info("新用户", zap.Any("wechatInfo", info))
	err = svc.repo.Create(ctx, domain.User{WechatInfo: info})
	if err != nil && !errors.Is(err, repository.ErrDuplicateUser) {
		return domain.User{}, err
	}
	return svc.repo.FindByWechat(ctx, info.OpenId)
}
